package org.example.java.util.concurrency;

import java.util.concurrent.TimeUnit;

/**
 * <a href="https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java">Double-checked_locking</a>
 *
 * @author JuntaoZhang
 * @since 2015/10/26 10:13
 */
public class DoubleCheckedLocking {
  static class Helper {
    private Boolean inited = false;

    public Helper() {
      System.out.println(Thread.currentThread().getName() + " Helper call construction.");
      try {
        TimeUnit.SECONDS.sleep(3);
      } catch (InterruptedException ignore) {
      }
      inited = true;
    }
  }

  static class Foo {
    private volatile Helper helper;

    public Helper getHelper() {
      if (helper == null) {
        System.out.println(Thread.currentThread().getName() + " Helper is null.");
        synchronized (this) {
          System.out.println(Thread.currentThread().getName() + " getHelper synchronized.");
          if (helper == null) {
            System.out.println(Thread.currentThread().getName() + " begin init Helper.");
            helper = new Helper();
          }
        }
      }
      return helper;
    }
  }

  static class Foo1 {
    private volatile Helper helper;

    /**
     * before jdk1.4 we should do this
     */
    public Helper getHelper() {
      Helper result = helper;
      if (result == null) {
        synchronized (this) {
          result = helper;
          if (result == null) {
            helper = result = new Helper();
          }
        }
      }
      return result;
    }
  }

  // Correct lazy initialization in Java
  static class Foo2 {
    private static class HelperHolder {
      public static final Helper helper = new Helper();
    }

    public static Helper getHelper() {
      System.out.println(Thread.currentThread().getName() + " getHelper.");
      return HelperHolder.helper;
    }
  }

  /*Semantics of final field in Java 5 can be employed to safely publish the helper object without using volatile*/
  static class FinalWrapper<T> {
    public final T value;
    public FinalWrapper(T value) {
      this.value = value;
    }
  }
  static class Foo3 {
    private FinalWrapper<Helper> helperWrapper;

    public Helper getHelper() {
      FinalWrapper<Helper> wrapper = helperWrapper;
      if (wrapper == null) {
        System.out.println(Thread.currentThread().getName() + " Helper is null.");
        synchronized(this) {
          System.out.println(Thread.currentThread().getName() + " getHelper synchronized.");
          if (helperWrapper == null) {
            System.out.println(Thread.currentThread().getName() + " begin init FinalWrapper.");
            helperWrapper = new FinalWrapper<Helper>(new Helper());
          }
          wrapper = helperWrapper;
        }
      }
      return wrapper.value;
    }
  }

  public static void main(String[] args) {
    final Foo foo = new Foo();
    Thread t1 = new Thread("t1") {
      @Override
      public void run() {
        System.out.println(Thread.currentThread().getName() + " call getHelper");
        Helper helper = foo.getHelper();
        System.out.println(Thread.currentThread().getName() + " helper inited=" + helper.inited);
      }
    };
    t1.start();
    try {
      TimeUnit.MILLISECONDS.sleep(300);
    } catch (InterruptedException ignore) {
    }
    Thread t2 = new Thread("t2") {
      @Override
      public void run() {
        System.out.println(Thread.currentThread().getName() + " call getHelper");
        Helper helper = foo.getHelper();
        System.out.println(Thread.currentThread().getName() + " helper inited=" + helper.inited);
      }
    };
    t2.start();
  }
}
//Foo getHelper
/*
t1 call getHelper
t1 Helper is null.
t1 getHelper synchronized.
t1 begin init Helper.
t1 Helper call construction.
t2 call getHelper
t2 Helper is null.(block here)
t2 getHelper synchronized.
t1 helper inited=true
t2 helper inited=true
 */

//Foo2 getHelper
/*
t1 call getHelper
t1 getHelper.
t1 Helper call construction.
t2 call getHelper
t2 getHelper.(block here)
t1 helper inited=true
t2 helper inited=true
*/

//Foo3 getHelper
/*
t1 call getHelper
t1 Helper is null.
t1 getHelper synchronized.
t1 begin init FinalWrapper.
t1 Helper call construction.
t2 call getHelper
t2 Helper is null.(block here)
t1 helper inited=true
t2 getHelper synchronized.
t2 helper inited=true
 */